const { assert, expect } = require("chai");
const { ethers, deployments, getNamedAccounts } = require("hardhat");
const { mine} = require("@nomicfoundation/hardhat-network-helpers");
const {developmentChains} = require("../../help-hardhat-config")

!developmentChains.includes(network.name)
    ? describe.skip
    :
describe("test-fundme-contract", async function () {
    // tip: 在开发的时候发现很多函数都需要加await 这是一个要注意的，另一个是(await getNamedAccounts()) 这个要写括号
    let firstAccount;
    let secondAccount;
    let fundmeDeploy;
    let mockV3AggregatorDeploy;
    let fundMeSecondAccount;

    // 每个it都会执行
    this.beforeEach(async function () {
        await deployments.fixture(["all"]);
        firstAccount = (await getNamedAccounts()).firstAccount;
        secondAccount = (await getNamedAccounts()).secondAccount;

        fundmeDeploy = await deployments.get("FundMe");
        mockV3AggregatorDeploy = await deployments.get("MockV3Aggregator");
        // 拿到地址获取合约
        fundMe = await ethers.getContractAt("FundMe", fundmeDeploy.address);

        // 以第二个账户操作fundMe合约
        // 这儿需要安装额外的包 npm install --save-dev @nomicfoundation/hardhat-ethers ethers hardhat-deploy hardhat-deploy-ethers
        // 然后在hardhat.config 引入
        fundMeSecondAccount = await ethers.getContract("FundMe", secondAccount)
    });

    it("test if ther owner is msg.sender", async function () {
        // let fundMeFactory = await ethers.getContractFactory("FundMe");
        // console.log("Deploying FundMe contract...");
        // let fundMe = await fundMeFactory.deploy(100);
        await fundMe.waitForDeployment();
        let owner = await fundMe.owner();
        // let [firstAccount] = await ethers.getSigners();
        assert.equal(owner, firstAccount);
    });

    // 比较mockV3AggregatorDeployments.address 和 fundMe.dataFeed()是否相等 也就是mock部署的地址
    it("test if the datafeed is assigned correctly", async function () {
        let dataFeed = await fundMe.dataFeed();
        assert.equal(dataFeed, mockV3AggregatorDeploy.address);
    })

    // fund, getFund, refund
    // unit test for fund
    // window open, value greater then minimum value, funder balance
    it("window closed, value grater than minimum, fund failed",
        async function () {
            // 断言抛出错误信息
            await mine(200, { interval: 1 }); // 100秒 = 100个区块，每个区块间隔1秒
            // expect 只能预测会失败，至于提示什么，好像不准
            await expect(fundMe.fund({ value: ethers.parseEther("1") }))
                .to.be.revertedWith("window is closed");
        }
    )

    it("window open, value is less than minimum, fund failed",
        async function () {
            await expect(
                fundMe.fund({ value: ethers.parseEther("0.0000001") }))
                .to.be.revertedWith("Send more ETH");
        }
    )

    it("Window open, value is greater minimum, fund success",
        async function () {
            await fundMe.fund({ value: ethers.parseEther("1") })
            let balance = await fundMe.fundersToAmount(firstAccount);
            assert.equal(balance, ethers.parseEther("1"));
        }

    )

    // // unit test for getFund  投资期结束后，目标达到后，投资人可以提现
    // // onlyOwner, windowClose, target reached
    it("not onwer, window closed, target reached, getFund failed",
        async function () {
            // 1、第一个账户直接转大量的钱
            // 2、等待超过窗口期
            // 3、用第二个账户提现

            await fundMe.fund({ value: ethers.parseEther("100") })
            await mine(200, { interval: 1 }); // 100秒 = 100个区块，每个区块间隔1秒
            await expect(
                fundMeSecondAccount.getFund())
                .to.be.revertedWith("this function can only be called by owner");


        }
    )

    // 时间还在投资期不能退款
    it("window open, target reached, getFund failed",
        async function () {
            await fundMe.fund({ value: ethers.parseEther("100") })
            await expect(
                fundMe.getFund())
                .to.be.revertedWith("window is not closed");
        }
    )

    // 时间到了，但是没有达到目标
    it("window open, target not reached, getFund failed",
        async function () {
            await mine(200, { interval: 1 }); // 100秒 = 100个区块，每个区块间隔1秒
            await expect(
                fundMe.getFund())
                .to.be.revertedWith("Target is not reached");
        }
    )

    it("window closed, target reached, getFund success",
        async function () {
            await fundMe.fund({ value: ethers.parseEther("100") })
            await mine(200, { interval: 1 }); // 100秒 = 100个区块，每个区块间隔1秒

            await expect(fundMe.getFund())
                .to.emit(fundMe, "FundWithdrawByOwner")
                .withArgs(ethers.parseEther("100"))

        }
    )

    // // refund
    // // windowClosed, target not reached, funder has balance
    it("window open, target not reached, funder has balance",
        async function () {
            await fundMe.fund({ value: ethers.parseEther("0.1") })
            await expect(
                fundMe.refund())
                .to.be.revertedWith("window is not closed");
        }
    )

    it("window closed, target reach, funder has balance",
        async function () {
            await fundMe.fund({ value: ethers.parseEther("1") })
            await mine(200, { interval: 1 });

            await expect(
                fundMe.refund())
                .to.be.revertedWith("Target is reached");
        }
    )

    it("window closed, target not reach, funder does not has balance",
        async function () {
            await fundMeSecondAccount.fund({ value: ethers.parseEther("0.1") })
            await mine(200, { interval: 1 });

            await expect(
                fundMe.refund())
                .to.be.revertedWith("there is no fund for you");
        }
    )

    it("window closed, target not reached, funder has balance",
        async function () {
            await fundMeSecondAccount.fund({ value: ethers.parseEther("0.1") })
            await mine(200, { interval: 1 });
            
            // 事件 账户和余额
            await expect(fundMeSecondAccount.refund())
            .to.emit(fundMeSecondAccount, "RefundByFunder")
            .withArgs(secondAccount, ethers.parseEther("0.1"))
        }
    )
})